/* 
 * File:   nametable.cpp
 * Author: ScaV
 * 
 * Created on October 26, 2011, 5:18 PM
 */

#include "nametable.h"
#include <cstring>
#include <iostream>
#include <fstream>
#include <cstdlib>

nametable::nametable(int sz) {
	if (size <= 0)	throw "Invalid table size!";
	
	size = sz;
	fill = 0;
	resizes = 0;
	
	depth = new int[size];
	table = new std::vector<char>*[size];
	index = new long*[size];
	Rand8 = new unsigned[32];
	
	memset(depth, 0, size * sizeof (int));
	memset(table, 0, size * sizeof (std::vector<char>*));
	memset(index, 0, size * sizeof (long*));
	
	srand((int)Rand8);
	for (int i = 0; i <  32; ++i)
		Rand8[i] = rand();
}

nametable::nametable(const nametable& orig) {
}

nametable::~nametable() {
	delete[] depth;
	delete[] Rand8;
	for (int i = 0; i < size; ++i)
		if (table[i]) {
			delete table[i];
			delete index[i];
		}
	delete[] table;
	delete[] index;
}

int nametable::add(lexeme* lx) {
	int num, i;
	while (1) {
		for (i = 0; ; ++i) {
			num = hash(i, lx->str);
			if (!table[num]) {
				table[num] = lx->str;
				index[num] = new long;
				*index[num] = num;
				depth[num] = i + 1;
				lx->index = index[num];
				++fill;
				if (fill > size / 2) {
					resize();
					++resizes;
				}
				return num;
			} else if (*table[num] == *(lx->str)) {
				delete (lx->str);
				lx->str = table[num];
				lx->index = index[num];
				return num;
			}
		}
		throw "Invalid fill check!";
	}
}

bool nametable::print(const char *name) {
	std::ofstream file;
	if (!name)	return false;
	file.open(name, std::ios::out);
	if (!file.is_open())	return false;
#ifdef HASHTABLE_DEBUG
	unsigned long calls = 0;
	for (int i = 0; i < size; ++i)
		calls += depth[i];
	file << "Calls/Fill/Size: " << calls << "/" << fill << "/" << size << std::endl;
	file << "Resizes: " << resizes << "\nRand8={";
	for (int i = 0; i < 32; ++i) {
		if (!(i % 16))
			file << std::endl;
		file << static_cast <int> (Rand8[i]);
		if (i != 31)
			file << ", ";
		else 
			file << "}\n\n";
	}
#endif
	for (int i = 0; i < size; ++i) {
		file << "[" << i << "]\t\t";
		if (!table[i]) {
			file << "-----" << std::endl;
			continue;
		}
		file << "<";
		std::vector<char>::iterator iter;
		for (iter = table[i]->begin(); iter != table[i]->end(); ++iter)
			file <<*iter;
#ifdef HASHTABLE_DEBUG
		file << ">\tC:" << depth[i];
#endif
		file << std::endl;
	}
	file.close();
	return true;
}

void nametable::resize() {
	
	std::vector<char> **tmpt;
	int *tmpd, ind;
	long **tmpi;
	int oldsize = size, j;
	
	tmpd = depth;
	tmpt = table;
	tmpi = index;
	
	size *= multiple;
	depth = new int[size];
	table = new std::vector<char>*[size];
	index = new long*[size];

	memset(depth, 0, size * sizeof (int));
	memset(table, 0, size * sizeof (std::vector<char>*));
	memset(index, 0, size * sizeof (long*));
	
	for (int i = 0; i < oldsize; ++i) {
		if (!tmpt[i])
			continue;
		for (j = 0; j <= size; ++j) {
			if (j == size)
				throw "Can't add an element!";
			ind = hash(j, tmpt[i]);
			if (!table[ind]) {
				table[ind] = tmpt[i];
				depth[ind] = tmpd[i] + j + 1;
				index[ind] = tmpi[i];
				*index[ind] = ind;
				break;
			} else if (*(table[ind]) == *(tmpt[i]))
				throw "Element duplicate!";
		}
	}
	delete[] tmpd;
	delete[] tmpt;
	delete[] tmpi;
	return;
}

int nametable::hash(int num, std::vector<char> *str) {
    unsigned hashVal = 0;
	std::vector<char>::iterator i;
    if (str == NULL) return 0;
    for (i = str->begin(); i != str->end(); ++i)
		hashVal ^= (hashVal << 5) + (hashVal >> 2) + *i;
    return (hashVal + Rand8[num % 31]) % size;
}
